package xalan.smartgwt.criteria;

import java.util.TreeMap;
import java.util.Map;

public class CriteriaSQLGenerator implements CriteriaVisitator {
	
	public static final String PERCENT = "'%'";
	public static final String STRING_CONCAT = "||";
	
	private StringBuilder builder;
	private String lineSeparator = " ";
	private Map<Integer, Object> parametersMap;
	private String toUpperFunctionName = "upper";
	
	public Map<Integer, Object> getParametersMap() {
		return parametersMap;
	}

	public void setLineSeparator(String lineSeparator) {
		this.lineSeparator = lineSeparator;
	}

	public void setToUpperFunctionName(String toUpperFunctionName) {
		this.toUpperFunctionName = toUpperFunctionName;
	}

	public String generate(Criteria criteria) {
		this.builder = new StringBuilder();
		this.parametersMap = new TreeMap<Integer, Object>();
		criteria.visit(this);
		return builder.toString();
	}

	@Override
	public void visitValueCriteria(ValueCriteria criteria) {
		Operator op = criteria.getOperator();
		if(op.isCaseIntensitive()) {
			builder.append(this.toUpperFunctionName);
			builder.append("(");
		}
		builder.append( transformFieldName(criteria.getFieldName()) );
		if(op.isCaseIntensitive()) {
			builder.append(")");
		}
		builder.append(" ");
		if(op.isNegation()) {
			builder.append("NOT ");
		}
		builder.append(op.getOperator());
		builder.append(" ");
		if(op.isPercentPrefix()) {
			builder.append(PERCENT);
			builder.append(" ");
			builder.append(STRING_CONCAT);
			builder.append(" ");
		}
		if(op.isCaseIntensitive()) {
			builder.append(this.toUpperFunctionName);
			builder.append("(");
		}
		addParameter(criteria.getValue());
		if(op.isCaseIntensitive()) {
			builder.append(")");
		}
		if(op.isPercentPostfix()) {
			builder.append(" ");
			builder.append(STRING_CONCAT);
			builder.append(" ");
			builder.append(PERCENT);	
		}
	}

	@Override
	public void visitBetweenCriteria(BetweenCriteria criteria) {
		builder.append(transformFieldName(criteria.getFieldName()));
		builder.append(" ");
		builder.append(criteria.getOperator().getOperator());
		builder.append(" ");
		addParameter(criteria.getStart());
		builder.append(" AND ");
		addParameter(criteria.getEnd());
	}


	@Override
	public void beforeVisitAdvanceCriteria(AdvanceCriteria criteria) {
		builder.append(lineSeparator);
		builder.append("(");
		builder.append(lineSeparator);
	}


	@Override
	public void beforeChild(AdvanceCriteria criteria, Criteria child, int childNumber) {
		if(childNumber != 0) {
			builder.append(lineSeparator);
			builder.append(criteria.getOperator());
			builder.append(" ");
		}
	}


	@Override
	public void afterChild(AdvanceCriteria criteria, Criteria child, int childNumber) {
		//do nothing
	}


	@Override
	public void afterVisitAdvanceCriteria(AdvanceCriteria criteria) {
		builder.append(lineSeparator);
		builder.append(")");
	}

	@Override
	public void visitFieldCriteria(FieldCriteria criteria) {
		builder.append(transformFieldName(criteria.getFieldName()));
		builder.append(" ");
		builder.append(criteria.getOperator().getOperator());
		builder.append(" ");
		builder.append(transformFieldName(criteria.getOtherFieldName()));
	}

	@Override
	public void visitNullCriteria(NullCriteria criteria) {
		builder.append(transformFieldName(criteria.getFieldName()));
		builder.append(" ");
		builder.append(criteria.getOperator().getOperator());
	}
	
	protected String transformFieldName(String fieldName) {
		return fieldName;
	}
	
	private void addParameter(Object value) {
		int currentNumber = parametersMap.size() + 1;
		parametersMap.put(currentNumber, value);
		builder.append("?");
	}
}
